package org.redmagic.common;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.redmagic.common.Hierarchy.Deserializer;
import org.redmagic.common.Hierarchy.Serializer;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

@JsonSerialize(using=Serializer.class)
@JsonDeserialize(using=Deserializer.class)
public class Hierarchy implements Iterable<Set<String>> {

	private final List<Set<String>> hierarchy;
	
	private Hierarchy(List<Set<String>> hierarchy) {
		this.hierarchy = hierarchy;
	}
	
	public Iterator<Set<String>> iterator() {
		return hierarchy.iterator();
	}
	
	@Override
	public String toString() {
		return hierarchy.toString();
	}
	
	public static class Builder {
		
		private List<Set<String>> hierarchy;
		
		public Builder() {
			hierarchy = new ArrayList<Set<String>>();
		}
		
		public Hierarchy build() {
			return new Hierarchy(hierarchy);
		}
		
		public Builder withTags(Set<String> tags) {
			hierarchy.add(tags);
			return this;
		}
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((hierarchy == null) ? 0 : hierarchy.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Hierarchy other = (Hierarchy) obj;
		if (hierarchy == null) {
			if (other.hierarchy != null)
				return false;
		} else if (!hierarchy.equals(other.hierarchy))
			return false;
		return true;
	}
	
	protected static class Serializer extends JsonSerializer<Hierarchy> {
		@Override
		public void serialize(Hierarchy value, JsonGenerator jgen,
				SerializerProvider provider) throws IOException,
				JsonProcessingException {
			
			Iterator<Set<String>> iterator = value.iterator();
			
			jgen.writeStartArray();
			
			while (iterator.hasNext()) {
				jgen.writeStartArray();
				for (String tag : iterator.next()) {
					jgen.writeString(tag);
				}
				jgen.writeEndArray();
			}
			
			jgen.writeEndArray();
		}
	}
	
	protected static class Deserializer extends JsonDeserializer<Hierarchy> {
		@Override
		public Hierarchy deserialize(JsonParser jp,
				DeserializationContext ctxt) throws IOException,
				JsonProcessingException {
			
			// TODO clean this up
			@SuppressWarnings("unchecked")
			List<List<String>> hierarchy = jp.readValueAs(List.class);
			
			Hierarchy.Builder builder = new Hierarchy.Builder();
			for (List<String> tags : hierarchy) {
				builder.withTags(new HashSet<String>(tags));
			}
			
			return builder.build();
		}
	}
}
